using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

namespace DepSoft.Helpers.Extensions
{
	public static class LinqExtensions
	{
        //public static IEnumerable<T> Except<T>(this IEnumerable<T> items,
        //                                       IEnumerable<T> other)
        //{
        //    return items.Except(other, item => item);
        //}

		public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items,
		                                             IEnumerable<T> other,
		                                             Func<T, TKey> getKey)
		{
			return items.Except(other, getKey, getKey);
		}

		public static IEnumerable<T> Except<T, TOther, TKey>(this IEnumerable<T> items,
		                                                     IEnumerable<TOther> other,
		                                                     Func<T, TKey> getItemKey,
		                                                     Func<TOther, TKey> getOtherKey)
		{
			if (items == null)
			{
				throw new ArgumentNullException("items", "list being selected against cannot be null");
			}
			if (getItemKey == null)
			{
				throw new ArgumentNullException("getItemKey", "please provide a method for determining item equality");
			}
			if (getOtherKey == null)
			{
				throw new ArgumentNullException("getOtherKey", "please provide a method for determining other item equality");
			}
			if (other == null || !other.Any() || !items.Any())
			{
				return items;
			}

			return from item in items
			       join otherItem in other on getItemKey(item) equals getOtherKey(otherItem) into tempItems
			       from temp in tempItems.DefaultIfEmpty()
			       where ReferenceEquals(null, temp) || temp.Equals(default(TOther))
			       select item;
		}

        public static IEnumerable<T> GetItems<T>(this IEnumerable collection, Func<T, IEnumerable> selector)
        {
            Stack<IEnumerable<T>> stack = new Stack<IEnumerable<T>>();
            stack.Push(collection.OfType<T>());

            while (stack.Count > 0)
            {
                IEnumerable<T> items = stack.Pop();
                foreach (var item in items)
                {
                    yield return item;

                    IEnumerable<T> children = selector(item).OfType<T>();
                    stack.Push(children);
                }
            }
        }
	}
}